﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Navigation;
using System.Data;
using System.Collections;
using Sinacor.UI.Bovespa.Ordens.Presenter;
using Sinacor.Infra.UI.Controls;
using Sinacor.UI.Bovespa.Ordens.Model;
using System.Text;
using System.ComponentModel;
using System.Collections.Generic;
using System.Globalization;
using System.Windows.Input;
using System.Text.RegularExpressions;
using Sinacor.Infra.UI.Controls.SmartClient;
using Sinacor.Infra.UI.Common;
using PSE.Framework.UI.Resources;
using Sinacor.UI.Bovespa.Ordens.Communication.Bovespa.Ordens.Ordem;
using Sinacor.UI.Bovespa.Ordens.Model.Enum;

namespace Sinacor.UI.Bovespa.Ordens.View.SmartClient
{
    /// <summary>
    /// Permite o cadastro de ajuste para o mercado futuro, 
    /// bem como o cálculo para atualização da posição do cliente para o ativo informado
    /// </summary>
    public partial class AjusteMercadoFuturo : SinacorWindow, IAjusteMercadoFuturo
    {
        #region Presenter

        /// <summary>
        /// Propriedade com a presenter da view
        /// </summary>
        public AjusteMercadoFuturoPresenter ajusteMercadoFuturoPresenter { get; set; }

        #endregion

        #region Construtor

        public AjusteMercadoFuturo()
        {
            // Este deve ser o primeiro método do contrutor da View
            InitializeComponent();

            // Inicialize a presenter no construtor
            this.ajusteMercadoFuturoPresenter = new AjusteMercadoFuturoPresenter(this);

            // Configure a presenter no DataContext da janela (para facilitar os bindings)
            this.DataContext = this.ajusteMercadoFuturoPresenter;

            this.dtgAjusteMercadoFuturo.NewItemType = typeof(Model.AjusteMercadoFuturo);

            this.AfterInitialize += new RoutedEventHandler(AjusteMercadoFuturo_AfterInitialize);
        }

        #endregion

        #region Métodos Públicos

        /// <summary>
        /// Realizar um UpdateTarget da propriedade ItemsSource do SinacorComboBox de pregões
        /// </summary>
        public void ApresentarListaDataPregao()
        {
            this.cmbFiltroDataPregao.UpdateTarget(SinacorComboBox.ItemsSourceProperty);
                this.SelecionaDataPregaoAtual(cmbFiltroDataPregao);            
        }

        /// <summary>
        /// Realiza o UpdateTarget no SinacorDataGrid
        /// </summary>
        public void ApresentarListaAjustesMercadoFuturo()
        {
            dtgAjusteMercadoFuturo.UpdateTarget(SinacorDataGrid.ItemsSourceProperty);
        }

        /// <summary>
        /// Realiza o UpdateTarget no SinacorTextBox Ativo do filtro de pesquisa
        /// </summary>
        /// <param name="titulo"></param>
        public void ExibirTituloEscolhido()
        {
            this.txtAtivo.UpdateTarget(SinacorTextBox.TextProperty);
        }

        /// <summary>
        /// Apresenta dados do ativo no DataGrid
        /// </summary>
        /// <param name="fatorCotacao">Fator de cotação</param>
        /// <param name="precoExercicio">Preço do exercício</param>
        public void ApresentarDadosAtivo(int fatorCotacao, decimal precoExercicio)
        {
            SinacorDecimalTextBox txtPrecoExerc = null;
            SinacorDecimalTextBox txtFator = null;

            if (dtgAjusteMercadoFuturo.EditIndex > -1)
            {
                // Alteração
                txtPrecoExerc = (SinacorDecimalTextBox)ControlHelper.FindChild(dtgAjusteMercadoFuturo.GetRow(dtgAjusteMercadoFuturo.EditIndex), "txtPrecoExercicio");
                txtFator = (SinacorDecimalTextBox)ControlHelper.FindChild(dtgAjusteMercadoFuturo.GetRow(dtgAjusteMercadoFuturo.EditIndex), "txtFatorCotacao");
            }
            else
            {
                // Inclusão
                txtPrecoExerc = (SinacorDecimalTextBox)ControlHelper.FindChild(dtgAjusteMercadoFuturo.GetInsertionRow(), "txtPrecoExercicio");
                txtFator = (SinacorDecimalTextBox)ControlHelper.FindChild(dtgAjusteMercadoFuturo.GetInsertionRow(), "txtFatorCotacao");
            }

            if (txtPrecoExerc != null)
                txtPrecoExerc.Value = precoExercicio;

            if (txtFator != null)
                txtFator.Value = fatorCotacao;
        }

        /// <summary>
        /// Apresenta a variação no DataGrid
        /// </summary>
        /// <param name="ajusteAplicado">Valor do ajuste aplicado</param>
        /// <param name="percentualVariacao">Percentual de variação</param>        
        public void ApresentarVariacao(decimal ajusteAplicado, decimal percentualVariacao)
        {
            SinacorDecimalTextBox txtAjuste = null;
            SinacorDecimalTextBox txtPercentualVariacao = null;

            if (dtgAjusteMercadoFuturo.EditIndex > -1)
            {
                // Alteração, capturando
                txtAjuste = (SinacorDecimalTextBox)ControlHelper.FindChild(dtgAjusteMercadoFuturo.GetRow(dtgAjusteMercadoFuturo.EditIndex), "textValorAjusteAplicado");
                txtPercentualVariacao = (SinacorDecimalTextBox)ControlHelper.FindChild(dtgAjusteMercadoFuturo.GetRow(dtgAjusteMercadoFuturo.EditIndex), "textVariacao");
            }
            else
            {
                // Inclusão, capturando
                txtAjuste = (SinacorDecimalTextBox)ControlHelper.FindChild(dtgAjusteMercadoFuturo.GetInsertionRow(), "textValorAjusteAplicado");
                txtPercentualVariacao = (SinacorDecimalTextBox)ControlHelper.FindChild(dtgAjusteMercadoFuturo.GetInsertionRow(), "textVariacao");
            }

            if (txtAjuste != null)
                txtAjuste.Value = ajusteAplicado;

            if (txtPercentualVariacao != null)
                txtPercentualVariacao.Value = Convert.ToDecimal(percentualVariacao.ToString("f5"));
        }

        /// <summary>
        /// Exibe mensagem de confirmação de ajuste de posição
        /// </summary>
        /// <returns></returns>
        public bool ConfirmarAjustePosicao()
        {
            return ExibirMensagemConfirmacao("Bovespa.Ordens.Message.ConfirmaAjustePosicao");
        }

        /// <summary>
        /// Exibe mensagem de confirmação de alteração do ajuste de mercado futuro
        /// </summary>
        /// <returns></returns>
        public bool ConfirmarAlteracao()
        {
            return ExibirMensagemConfirmacao("Bovespa.Ordens.Message.ConfirmaAlteracoes");
        }

        /// <summary>
        /// Exibe mensagem
        /// </summary>
        /// <param name="resourceKey"></param>
        public void ExibirMensagem(string resourceKey)
        {
            SinacorMessageBox.Show((string)ResourceProviderManager.GetResourceProvider().TryGetResource(resourceKey), SinacorMessageBoxImage.Information);
        }

        /// <summary>
        /// Informa mensagem de valor de ajuste anterior obrigatório
        /// </summary>
        public void InformarValorAjusteAnteriorObrigatorio()
        {
            this.ClearServiceErrors();
            this.ExibirMensagem("Bovespa.Ordens.Message.ValorInformadoInvalido");
        }

        /// <summary>
        /// Informa mensagem de valor atual obrigatório
        /// </summary>
        public void InformarValorAtualObrigatorio()
        {
            this.ClearServiceErrors();
            this.ExibirMensagem("Bovespa.Ordens.Message.ValorInformadoInvalido");
        }

        /// <summary>
        /// Informa que o ativo não pertence ao mercado futuro
        /// </summary>
        public void InformarAtivoNaoPertenceMercadoFuturo()
        {
            this.ExibirMensagem("Bovespa.Ordens.ExceptionMessage.AtivoNaoPertenceMercadoFuturo");
        }

        /// <summary>
        /// Informa que movimento está fechado e fechar janela
        /// </summary>
        public void InformarMovimentoFechado()
        {
            SinacorMessageBox.Show((string)ResourceProviderManager.GetResourceProvider().TryGetResource("Bovespa.Ordens.Message.MovOrdensNaoAberto"), SinacorMessageBoxImage.Exclamation);
            this.Close();
            return;
        }

        #endregion

        #region Métodos Privados

        /// <summary>
        /// Exibe mensagem de confirmação
        /// </summary>
        /// <param name="resourceKey">Mensagem</param>
        /// <returns>True ou False de acordo com a confirmação</returns>
        private bool ExibirMensagemConfirmacao(string resourceKey)
        {
            SinacorMessageBoxResult result;
            if (resourceKey == "Bovespa.Ordens.Message.ConfirmaAjustePosicao")
            {
                string msgFormatada = (string)ResourceProviderManager.GetResourceProvider().TryGetResource(resourceKey);
                StringBuilder sb = new StringBuilder();

                if (this.ajusteMercadoFuturoPresenter.Filtro.DataPregao.HasValue)
                    sb.AppendFormat(msgFormatada, this.ajusteMercadoFuturoPresenter.Filtro.DataPregao.Value.ToString("dd/MM/yyyy"));

                result = SinacorMessageBox.Show(sb.ToString(), SinacorMessageBoxButton.YesNo, SinacorMessageBoxImage.Question);
            }
            else
                result = SinacorMessageBox.Show((string)ResourceProviderManager.GetResourceProvider().TryGetResource(resourceKey), SinacorMessageBoxButton.YesNo, SinacorMessageBoxImage.Question);

            return (result == SinacorMessageBoxResult.Yes);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        private void SelecionaDataPregaoAtual(object sender)
        {
            if (((SinacorComboBox)sender).Items.Count > 0)
                ((SinacorComboBox)sender).SelectedIndex = 0;

            ((SinacorComboBox)sender).Focus();
        }


        #endregion

        #region Eventos

        #region AfterInitialize

        /// <summary>
        /// Event Handler disparado após a inicialização dos controles
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void AjusteMercadoFuturo_AfterInitialize(object sender, RoutedEventArgs e)
        {
            this.ShowUserWaitingProcess();
            this.ajusteMercadoFuturoPresenter.Inicializar();
            this.HideUserWaitingProcess();
        }

        #endregion

        #region Botões de Pesquisa

        /// <summary>
        /// Event Handler do botão de pesquisa de ativos
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnPesquisarAtivo_Click(object sender, RoutedEventArgs e)
        {
            if (this.ValidateContainer("PesquisaAtivo"))
            {
                Model.Titulo titulo = new Model.Titulo();
                titulo.DataPregao = this.ajusteMercadoFuturoPresenter.Filtro.DataPregao;
                titulo.Mercado = Model.Enum.TipoMercado.Futuro;

                PesquisaTitulo pesquisaTitulo = new PesquisaTitulo();
                pesquisaTitulo.CarregarParametros(titulo, TelaChamadora.AjusteMercadoFuturo);
                pesquisaTitulo.Owner = this;
                pesquisaTitulo.SetMenuHeader("Bovespa.Ordens.Label.PesquisaTitulo");
                pesquisaTitulo.ShowDialog();

                this.ajusteMercadoFuturoPresenter.AtualizarTituloEscolhido(pesquisaTitulo.TituloEscolhido);
            }
        }

        /// <summary>
        /// Event Handler de pesquisa de ajustes de mercado futuro
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnPesquisar_Click(object sender, RoutedEventArgs e)
        {
            if (!dtgAjusteMercadoFuturo.IsEditing)
            {
                this.ShowUserWaitingProcess();
                this.ajusteMercadoFuturoPresenter.ConsultarAjustesMercadoFuturo();
                this.HideUserWaitingProcess();
            }
        }

        /// <summary>
        /// Event Handler para nova pesquisa
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnNovaPesquisa_Click(object sender, RoutedEventArgs e)
        {
            this.ApresentarListaDataPregao();
            if (this.dtgAjusteMercadoFuturo.IsEditing)
                this.dtgAjusteMercadoFuturo.CancelUpdate();

            if (this.dtgAjusteMercadoFuturo.IsInserting)
                this.dtgAjusteMercadoFuturo.CancelInsert(); 

            this.ajusteMercadoFuturoPresenter.Filtro.CodigoNegociacao = string.Empty;
            txtAtivo.UpdateTarget(SinacorTextBox.TextProperty);

            if (this.ajusteMercadoFuturoPresenter.ListaAjusteMercadoFuturo != null)
            {
                this.ajusteMercadoFuturoPresenter.ListaAjusteMercadoFuturo.Clear();
                this.ApresentarListaAjustesMercadoFuturo();
            }
        }

        #endregion

        #region Filtro

        private void txtAtivo_PreviewLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
        {
            if (e.NewFocus.GetType () == btnNovaPesquisa.GetType()  ) 
            {
                SinacorButton limpar = ( SinacorButton ) e.NewFocus;
                if (limpar.Name == btnNovaPesquisa.Name)
                {
                    e.Handled = true;
                    return; 
                }
            }
            this.ClearServiceErrors();
            if (((SinacorTextBox)sender).Text.Trim() != string.Empty)
            {
                
                this.ajusteMercadoFuturoPresenter.Filtro.CodigoNegociacao = ((SinacorTextBox)sender).Text;
                e.Handled = !ajusteMercadoFuturoPresenter.ValidarFiltroAtivo();
            }
        }

        #endregion

        #region DataGrid

        /// <summary>
        /// Event Handler do DataGrid disparado ao inserir uma nova linha
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void dtgAjusteMercadoFuturo_InsertBegun(object sender, ItemEventArgs e)
        {
            if (e.OriginalSource is InsertionRow)
            {
                this.ClearServiceErrors();
                SinacorComboBox comboGrid = null;
                comboGrid = (SinacorComboBox)ControlHelper.FindChild(dtgAjusteMercadoFuturo.GetInsertionRow(), "cmbDataPregao");
                comboGrid.UpdateTarget(SinacorComboBox.ItemsSourceProperty);

                comboGrid.Loaded += new RoutedEventHandler(cmbDataPregao_Loaded);

                this.SelecionaDataPregaoAtual(comboGrid);
            }
        }

        /// <summary>
        /// Event Handler disparado pelo item "Salvar Tudo" do contextmenu do DataGrid
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void dtgAjusteMercadoFuturo_SavingAll(object sender, CancelItemsEventArgs e)
        {
            this.ClearServiceErrors();

            if (this.ajusteMercadoFuturoPresenter.ListaAjusteMercadoFuturo.HasChanges)
            {
                if (this.ConfirmarAlteracao())
                {
                    this.ShowUserWaitingProcess();

                    this.ajusteMercadoFuturoPresenter.Salvar();

                    if (this.ErrorMessages.Count > 0)
                        e.Cancel = true;

                    this.HideUserWaitingProcess();
                }
                else
                    e.Cancel = true;
            }
        }

        /// <summary>
        /// Event Handler disparado pelo item "Desfazer Tudo" do contextmenu do DataGrid
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void dtgAjusteMercadoFuturo_UndoingAll(object sender, CancelItemsEventArgs e)
        {
            SinacorMessageBoxResult result = SinacorMessageBox.Show((string)ResourceProviderManager.GetResourceProvider().TryGetResource("Bovespa.Ordens.Message.DesfazerAlteracoes"), SinacorMessageBoxButton.YesNo);
            if (result == SinacorMessageBoxResult.No)
                e.Cancel = true;
        }

        /// <summary>
        /// Event Handler disparado após uma linha existente no datagrid entrar em modo de edição
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void dtgAjusteMercadoFuturo_UpdateBegun(object sender, ItemEventArgs e)
        {
            if (e.OriginalSource is Row)
            {
                this.ClearServiceErrors();
                SinacorDecimalTextBox valorAjusteAnterior = null;
                valorAjusteAnterior = (SinacorDecimalTextBox)ControlHelper.FindChild(dtgAjusteMercadoFuturo.GetRow(dtgAjusteMercadoFuturo.EditIndex), "textValorAjusteAnterior");
                valorAjusteAnterior.Focus();
            }
        }

        #endregion

        #region Controles DataGrid

        /// <summary>
        /// Event Handler disparado previamente à perda do foco do SinacorTextBox de Ativo do DataGrid
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void textCodigoNegociacao_PreviewLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
        {
            if (e.NewFocus.GetType() == btnNovaPesquisa.GetType())
            {
                SinacorButton limpar = (SinacorButton)e.NewFocus;
                if (limpar.Name == btnNovaPesquisa.Name)
                {
                    e.Handled = true;
                    return;
                }
            }
            if (((SinacorTextBox)sender).Text.Trim() != string.Empty)
            {               
                this.ClearServiceErrors();
                SinacorComboBox comboGrid = null;

                if (dtgAjusteMercadoFuturo.EditIndex > -1)
                    //Alteracao
                    comboGrid = (SinacorComboBox)ControlHelper.FindChild(dtgAjusteMercadoFuturo.GetRow(dtgAjusteMercadoFuturo.EditIndex), "cmbDataPregao");
                else
                    //Insercao
                    comboGrid = (SinacorComboBox)ControlHelper.FindChild(dtgAjusteMercadoFuturo.GetInsertionRow(), "cmbDataPregao");

                ajusteMercadoFuturoPresenter.Filtro.CodigoNegociacao = ((SinacorTextBox)sender).Text;

                if (comboGrid.SelectedValue != null && comboGrid.SelectedIndex > -1)                
                    ajusteMercadoFuturoPresenter.Filtro.DataPregao = Convert.ToDateTime(comboGrid.SelectedValue);                                    

                if (!this.ajusteMercadoFuturoPresenter.ValidarAtivo())
                    e.Handled = true;
            }
        }

        /// <summary>
        /// Event Handler disparado previamente à perda do foco do SinacorDecimalTextBox ("valor ajuste anterior") do DataGrid
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void textValorAjusteAnterior_PreviewLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
        {
            if (e.NewFocus.GetType() == btnNovaPesquisa.GetType())
            {
                SinacorButton limpar = (SinacorButton)e.NewFocus;
                if (limpar.Name == btnNovaPesquisa.Name)
                {
                    e.Handled = true;
                    return;
                }
            }
            if (((SinacorDecimalTextBox)sender).Value.HasValue)
            {
                if (this.ajusteMercadoFuturoPresenter.ValidarValorAjusteAnterior(((SinacorDecimalTextBox)sender).Value))
                {
                    Model.AjusteMercadoFuturo ajusteMercadoFuturo = new Sinacor.UI.Bovespa.Ordens.Model.AjusteMercadoFuturo();

                    if (((SinacorDecimalTextBox)sender).Value.HasValue)
                        ajusteMercadoFuturo.ValorAjusteAnterior = ((SinacorDecimalTextBox)sender).Value;
                    else
                        ajusteMercadoFuturo.ValorAjusteAnterior = 0;

                    SinacorDecimalTextBox txtValorAtual = null;
                    if (dtgAjusteMercadoFuturo.EditIndex > -1)
                        //Atualização
                        txtValorAtual = (SinacorDecimalTextBox)ControlHelper.FindChild(dtgAjusteMercadoFuturo.GetRow(dtgAjusteMercadoFuturo.EditIndex), "textValorAjusteAtual");
                    else
                        //Inserção
                        txtValorAtual = (SinacorDecimalTextBox)ControlHelper.FindChild(dtgAjusteMercadoFuturo.GetInsertionRow(), "textValorAjusteAtual");

                    if (txtValorAtual != null)
                    {
                        if (txtValorAtual.Value.HasValue)
                            ajusteMercadoFuturo.ValorAjusteAtual = txtValorAtual.Value;
                        else
                            ajusteMercadoFuturo.ValorAjusteAtual = 0;
                    }
                    else
                        ajusteMercadoFuturo.ValorAjusteAtual = 0;

                    this.ajusteMercadoFuturoPresenter.CalcularVariacaoAjusteAnterior(ajusteMercadoFuturo);
                }
                else
                    e.Handled = true;
            }
        }

        /// <summary>
        /// Event Handler disparado previamente à perda do foco do SinacorDecimalTextBox ("valor ajuste atual") do DataGrid
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void textValorAjusteAtual_PreviewLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
        {

            if (e.NewFocus.GetType() == btnNovaPesquisa.GetType())
            {
                SinacorButton limpar = (SinacorButton)e.NewFocus;
                if (limpar.Name == btnNovaPesquisa.Name)
                {
                    e.Handled = true;
                    return;
                }
            }
            if (((SinacorDecimalTextBox)sender).Value.HasValue)
            {
                if (this.ajusteMercadoFuturoPresenter.ValidarValorAjusteAtual(((SinacorDecimalTextBox)sender).Value))
                {
                    Model.AjusteMercadoFuturo ajusteMercadoFuturo = new Sinacor.UI.Bovespa.Ordens.Model.AjusteMercadoFuturo();
                    if (((SinacorDecimalTextBox)sender).Value.HasValue)
                        ajusteMercadoFuturo.ValorAjusteAtual = ((SinacorDecimalTextBox)sender).Value;
                    else
                        ajusteMercadoFuturo.ValorAjusteAtual = 0;

                    SinacorDecimalTextBox txtValorAnterior = null;

                    if (dtgAjusteMercadoFuturo.EditIndex > -1)
                        //Atualização
                        txtValorAnterior = (SinacorDecimalTextBox)ControlHelper.FindChild(dtgAjusteMercadoFuturo.GetRow(dtgAjusteMercadoFuturo.EditIndex), "textValorAjusteAnterior");
                    else
                        //Inserção
                        txtValorAnterior = (SinacorDecimalTextBox)ControlHelper.FindChild(dtgAjusteMercadoFuturo.GetInsertionRow(), "textValorAjusteAnterior");

                    if (txtValorAnterior != null)
                    {
                        if (txtValorAnterior.Value.HasValue)
                            ajusteMercadoFuturo.ValorAjusteAnterior = txtValorAnterior.Value;
                        else
                            ajusteMercadoFuturo.ValorAjusteAnterior = 0;
                    }
                    else
                        ajusteMercadoFuturo.ValorAjusteAnterior = 0;

                    this.ajusteMercadoFuturoPresenter.CalcularVariacaoAjusteAtual(ajusteMercadoFuturo);
                }
                else
                    e.Handled = true;
            }
        }       
        
        /// <summary>
        /// Event Hadler disparado após o carregamento do SinacorComboBox("data do pregão") do DataGrid
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void cmbDataPregao_Loaded(object sender, RoutedEventArgs e)
        {
            this.SelecionaDataPregaoAtual(sender);
        }

        #endregion

        #region ContextMenu

        /// <summary>
        /// Event Handler disparado após o click no menucontextual "Ajustar Posição" do DataGrid
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void AjustarPosicao_Click(object sender, RoutedEventArgs e)
        {
            this.ClearServiceErrors();

            this.ShowUserWaitingProcess();
            if (this.ajusteMercadoFuturoPresenter.ListaAjusteMercadoFuturo.HasChanges)                         
                this.dtgAjusteMercadoFuturo.SaveAll();                                            
            this.ajusteMercadoFuturoPresenter.AjustarPosicao();
            this.HideUserWaitingProcess();
        }

        #endregion

        #endregion
    }
}